
#ifndef TOKZRGENTR_UTILS_CHAR_SET_TABLE_H
#define TOKZRGENTR_UTILS_CHAR_SET_TABLE_H
#include"asserts.h"
#include"ucs2_ranges.h"
#include<memory>
#include<algorithm>
#include<iterator>
//#	For split the ucs2_ranges 

namespace TokzrGentr
{
	class char_sets_table;
	constexpr char16_t __segment_beg() { return 1u; };
	constexpr char16_t __segment_end() { return 2u; };
	constexpr char16_t __segment_point() { return 3u; };

	class char_sets_table
	{
	public:
		using value_type = char16_t;
		using iterator = value_type *;
		using ucs_type = char16_t;
	private:
		value_type hashing_table[65536];
		bool _is_constructed;
		std::set<value_type> value_full_set;
	public:
		char_sets_table()
			:_is_constructed(false), hashing_table{ 0 } {}
		void construct()
		{
			ASSERT(!_is_constructed);
			const size_t table_size = sizeof(hashing_table) / sizeof(hashing_table[0]);
			value_type count = 0;
			for (size_t i = 0; i < table_size;)
			{
				while (hashing_table[i] == 0)
					hashing_table[i++] = count;
				if (hashing_table[i] == __segment_beg())
				{
					count++;
					hashing_table[i++] = count;
					continue;
				}
				if (hashing_table[i] == __segment_end())
				{
					hashing_table[i++] = count;
					count++;
					continue;
				}
				if (hashing_table[i] == __segment_point())
				{
					if (i == 0)
					{
						hashing_table[i++] = count;
						count++;
						continue;
					}

					if (i < (table_size - 1) &&
						(hashing_table[i + 1] == __segment_beg()) ||
						hashing_table[i + 1] == __segment_point())
					{
						count++;
						hashing_table[i++] = count;
						continue;
					}
					count++;
					hashing_table[i++] = count;
					count++;
				}
			}
			value_type cnt = 0;
			value_type end_cnt = hashing_table[table_size - 1] + 1;
			while (cnt < end_cnt)
				value_full_set.insert(cnt++);
			_is_constructed = true;
		}
		void construct(std::set<std::shared_ptr<ucs2_ranges_set>> full_sets)
		{
			add(full_sets);
			construct();
		}
		void add(std::set<std::shared_ptr<ucs2_ranges_set>> partial_sets)
		{
			for (auto single_set : partial_sets)
				add(single_set);
		}
		void add(std::shared_ptr<ucs2_ranges_set> single_set)
		{
			ASSERT(!_is_constructed);
			for (auto range : *single_set)
			{
				auto beg = range.low();
				auto en = range.high();
				if (hashing_table[beg] == __segment_end() || hashing_table[beg] == __segment_point())
					hashing_table[beg] = __segment_point();
				else
					hashing_table[beg] = __segment_beg();

				if (hashing_table[en] == __segment_beg() || hashing_table[en] == __segment_point())
					hashing_table[en] = __segment_point();
				else
					hashing_table[en] = __segment_end();
			}
		}
		bool is_constructed() const
		{
			return _is_constructed;
		}
		value_type get(ucs_type codepoint)
		{
			ASSERT(_is_constructed);
			return hashing_table[codepoint];
		}
		std::set<value_type> get(std::shared_ptr<ucs2_ranges_set> single_set)
		{
			ASSERT(_is_constructed);
			std::set<value_type> value_set;
			for (auto range : *single_set)
			{
				auto val_beg = get(range.low());
				auto val_end = get(range.high());
				while (val_beg <= val_end)
					value_set.insert(val_beg++);
			}
			if (single_set->is_positive())
				return value_set;
			std::set<value_type> result;
			std::set_difference(
				value_full_set.begin(), value_full_set.end(),
				value_set.begin(), value_set.end(),
				std::insert_iterator<decltype(result)>(result, result.begin()));
			return result;
		}
		iterator begin() { return hashing_table; };
		iterator end() { return hashing_table + 65536; };
		value_type &operator[](size_t index) {
			ASSERT(_is_constructed);
			ASSERT(index >= 0 && index <= 65536);
			return hashing_table[index];
		}
		size_t value_num() {
			ASSERT(_is_constructed);
			return hashing_table[65535] + 1;
		}
		size_t size() const
		{
			return 65536;
		}
	};
}

#endif // !TOKZRGENTR_UTILS_CHAR_SET_TABLE_H
